package com.yeskery.nut.plugin;

import com.yeskery.nut.core.NutException;

import java.lang.reflect.Constructor;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.logging.Logger;

/**
 * 默认的插件管理器
 * @author sprout
 * 2022-05-12 19:41
 */
public class DefaultPluginManager implements PluginManager {

    /** 日志对象 */
    private static final Logger logger = Logger.getLogger(DefaultPluginManager.class.getName());

    /** controller插件 */
    private final List<ControllerPlugin> controllerPlugins = new CopyOnWriteArrayList<>();

    /** 拦截器插件 */
    private final List<InterceptorPlugin> interceptorPlugins = new CopyOnWriteArrayList<>();

    /** 异常处理插件 */
    private final List<ExceptionHandlePlugin> exceptionHandlePlugins = new CopyOnWriteArrayList<>();

    /** 服务器事件插件 */
    private final List<ServerEventPlugin> serverEventPlugins = new CopyOnWriteArrayList<>();

    /** 其他事件插件 */
    private final List<Plugin> otherPlugins = new CopyOnWriteArrayList<>();

    /** 缓存插件类型 */
    private final Set<Class<? extends Plugin>> cachePluginClassSet = new CopyOnWriteArraySet<>();

    @Override
    public Collection<Plugin> getAllPlugins() {
        List<Plugin> plugins = new LinkedList<>();
        plugins.addAll(controllerPlugins);
        plugins.addAll(interceptorPlugins);
        plugins.addAll(exceptionHandlePlugins);
        plugins.addAll(serverEventPlugins);
        plugins.addAll(otherPlugins);
        plugins.sort(Comparator.comparingInt(Plugin::getOrder));
        return Collections.unmodifiableCollection(plugins);
    }

    @Override
    public Collection<ControllerPlugin> getControllerPlugins() {
        List<ControllerPlugin> plugins = new ArrayList<>(controllerPlugins);
        plugins.sort(Comparator.comparing(ControllerPlugin::getOrder));
        return Collections.unmodifiableCollection(plugins);
    }

    @Override
    public Collection<InterceptorPlugin> getInterceptorPlugins() {
        List<InterceptorPlugin> plugins = new ArrayList<>(interceptorPlugins);
        plugins.sort(Comparator.comparing(InterceptorPlugin::getOrder));
        return Collections.unmodifiableCollection(plugins);
    }

    @Override
    public Collection<ExceptionHandlePlugin> getExceptionHandlePlugins() {
        List<ExceptionHandlePlugin> plugins = new ArrayList<>(exceptionHandlePlugins);
        plugins.sort(Comparator.comparing(ExceptionHandlePlugin::getOrder));
        return Collections.unmodifiableCollection(plugins);
    }

    @Override
    public Collection<ServerEventPlugin> getServerEventPlugins() {
        List<ServerEventPlugin> plugins = new ArrayList<>(serverEventPlugins);
        plugins.sort(Comparator.comparing(ServerEventPlugin::getOrder));
        return Collections.unmodifiableCollection(plugins);
    }

    @Override
    public void registerPlugin(Plugin plugin) {
        if (containPlugin(plugin.getClass())) {
            logger.warning("Plugin Class [" + plugin.getClass() + "] Already Registered.");
            return;
        }
        if (plugin instanceof ControllerPlugin) {
            controllerPlugins.add((ControllerPlugin) plugin);
        } else if (plugin instanceof InterceptorPlugin) {
            interceptorPlugins.add((InterceptorPlugin) plugin);
        } else if (plugin instanceof ExceptionHandlePlugin) {
            exceptionHandlePlugins.add((ExceptionHandlePlugin) plugin);
        } else if (plugin instanceof ServerEventPlugin) {
            serverEventPlugins.add((ServerEventPlugin) plugin);
        } else {
            otherPlugins.add(plugin);
        }
        cachePluginClassSet.add(plugin.getClass());
    }

    @Override
    public void registerPlugin(Class<? extends Plugin> pluginClass) {
        try {
            Constructor<? extends Plugin> constructor = pluginClass.getConstructor();
            Plugin plugin = constructor.newInstance();
            registerPlugin(plugin);
        } catch (Exception e) {
            throw new NutException("The Register Of Plugin Class Need No Parameter Constructor And Access Permission.", e);
        }
    }

    @Override
    public boolean containPlugin(Class<? extends Plugin> clazz) {
        return cachePluginClassSet.contains(clazz);
    }
}
